package com.mini.es.service;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.mini.es.model.EsBusinessStudentOrderInfo;
import com.mini.es.repository.EsBusinessStudentOrderInfoRepository;
import com.mini.vo.MemberDataCondition;
import com.mini.vo.MemberDataVO;
import com.mini.vo.MemberDataVO.Fields;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.LongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
import org.elasticsearch.search.aggregations.metrics.valuecount.InternalValueCount;
import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

import static com.mini.es.model.EsBusinessStudentOrderInfo.Fields.*;
import static org.elasticsearch.search.aggregations.AggregationBuilders.count;
import static org.elasticsearch.search.aggregations.AggregationBuilders.sum;

/**
 * @author anjl
 * @date 2021/2/26
 **/
@Slf4j
@Service
public class EsBusinessStudentOrderInfoServiceImpl implements EsBusinessStudentOrderInfoService {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Iterable<EsBusinessStudentOrderInfo> save(List<EsBusinessStudentOrderInfo> list) {
        if (Objects.isNull(list) || list.isEmpty()) {
            return null;
        }
        Date now = new Date();
        list.forEach(info -> {
            info.setMysqlUpdateTime(maxDate(info.getTeacherUpdateTime(), info.getAttachUpdateTime(), info.getOrderUpdateTime()));
            info.setUpdateTime(now);
        });
        return businessStudentOrderInfoRepository.saveAll(list);
    }

    @Override
    public EsBusinessStudentOrderInfo findById(String orderNo) {
        return businessStudentOrderInfoRepository.findById(orderNo).orElse(null);
    }

    public static Date maxDate(Date... dates) {
        if (Objects.isNull(dates) || dates.length == 0) {
            return null;
        }
        List<Date> collect = Arrays.stream(dates).sequential().filter(Objects::nonNull).collect(Collectors.toList());
        if (collect.isEmpty()) {
            return null;
        }
        Date result = null;
        for (int i = 0; i < collect.size() - 1; i++) {
            if (collect.get(i).compareTo(collect.get(i + 1)) > 0) {
                result = collect.get(i);
            } else {
                result = collect.get(i + 1);
            }
        }
        return result;
    }

    @Override
    public List<EsBusinessStudentOrderInfo> findAllById(List<String> ids) {
        ArrayList<EsBusinessStudentOrderInfo> list = Lists.newArrayList();
        businessStudentOrderInfoRepository.findAllById(ids).forEach(list::add);
        return list;
    }

    @Override
    public EsBusinessStudentOrderInfo findLatestUpdateTimeData() {
        Pageable pageable = PageRequest.of(1, 1, Sort.Direction.DESC, orderUpdateTime);
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.findAll(pageable);
        return page.getContent().isEmpty() ? null : page.getContent().get(0);
    }

    @Override
    public EsBusinessStudentOrderInfo findLatestAttachUpdateTimeData() {
        Pageable pageable = PageRequest.of(1, 1, Sort.Direction.DESC, attachUpdateTime);
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.findAll(pageable);
        return page.getContent().isEmpty() ? null : page.getContent().get(0);
    }

    @Override
    public EsBusinessStudentOrderInfo findLatestTeacherUpdateTimeData() {
        Pageable pageable = PageRequest.of(1, 1, Sort.Direction.DESC, teacherUpdateTime);
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.findAll(pageable);
        return page.getContent().isEmpty() ? null : page.getContent().get(0);
    }

    @Override
    public EsBusinessStudentOrderInfo findLatestClassUpdateTimeData() {
        Pageable pageable = PageRequest.of(1, 1, Sort.Direction.DESC, classUpdateTime);
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.findAll(pageable);
        return page.getContent().isEmpty() ? null : page.getContent().get(0);
    }

    @Override
    public EsBusinessStudentOrderInfo findLatestBaseUpdateTimeData() {
        Pageable pageable = PageRequest.of(1, 1, Sort.Direction.DESC, baseUpdateTime);
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.findAll(pageable);
        return page.getContent().isEmpty() ? null : page.getContent().get(0);
    }

    @Override
    public List<MemberDataVO> summaryDataByUser(MemberDataCondition condition) {

        List<MemberDataVO> result;
        NativeSearchQuery query = buildGroupSearch(
                condition.getLessonId(),
                condition.getOrderDateStart(),
                condition.getOrderDateEnd());

        result = elasticsearchTemplate.query(query, resp -> {
            //函数中传入SearchResponse对象，可以通过getAggregations获取聚合结果，然后解析后返回
            LongTerms terms = resp.getAggregations().get(teacherId);
            List<LongTerms.Bucket> buckets = terms.getBuckets();
            //遍历打印
            List<MemberDataVO> list = new ArrayList();
            for (LongTerms.Bucket bucket : buckets) {
                Aggregations a = bucket.getAggregations();
                MemberDataVO summary = buildResult(bucket, a);
                list.add(summary);
            }
            return list;
        });

        return result;
    }

//    @Override
//    public Map<Integer, List<Date>> groupByTeacher() {
//        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
//        //构建分组
//        TermsAggregationBuilder teacherBuilder = AggregationBuilders.terms(teacherId).field(teacherId);
//        TermsAggregationBuilder groupBuilder = AggregationBuilders.terms(teacherUpdateTime).field(teacherUpdateTime);
//        teacherBuilder.subAggregation(groupBuilder).size(Integer.MAX_VALUE);
//        NativeSearchQuery query = builder
//                .addAggregation(teacherBuilder)
//                .withSourceFilter(new FetchSourceFilter(new String[]{teacherId, teacherUpdateTime}, null))
//                .build();
//        return elasticsearchTemplate.query(query, resp -> {
//            //函数中传入SearchResponse对象，可以通过getAggregations获取聚合结果，然后解析后返回
//            LongTerms terms = resp.getAggregations().get(teacherId);
//            List<LongTerms.Bucket> buckets = terms.getBuckets();
//            //遍历打印
//            Map<Integer, List<Date>> map = new HashMap<>();
//            for (LongTerms.Bucket bucket : buckets) {
//                Aggregations a = bucket.getAggregations();
//                List<LongTerms.Bucket> sonBuckets = ((LongTerms) a.get(teacherUpdateTime)).getBuckets();
//                List<Date> teacherUpdateTimeList = sonBuckets.stream().map(son -> new Date(Long.parseLong(son.getKey().toString()))).collect(Collectors.toList());
//                map.put(Integer.parseInt(bucket.getKeyAsString()), teacherUpdateTimeList);
//            }
//            return map;
//        });
//    }

    @Override
    public Page<EsBusinessStudentOrderInfo> pageByTeacherId(Integer teacherId, Date teacherUpdateTime, Pageable pageable) {
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.must(QueryBuilders.matchQuery(EsBusinessStudentOrderInfo.Fields.teacherId, teacherId));
        qb.mustNot(QueryBuilders.matchQuery(EsBusinessStudentOrderInfo.Fields.teacherUpdateTime, teacherUpdateTime.getTime()));
        return businessStudentOrderInfoRepository.search(qb, pageable);
    }

    @Override
    public Page<EsBusinessStudentOrderInfo> pageByMiniLessonId(Long miniLessonId, Date baseUpdateTime, Pageable pageable) {
        BoolQueryBuilder qb = QueryBuilders.boolQuery();
        qb.must(QueryBuilders.matchQuery(EsBusinessStudentOrderInfo.Fields.miniLessonId, miniLessonId));
        qb.mustNot(QueryBuilders.matchQuery(EsBusinessStudentOrderInfo.Fields.baseUpdateTime, baseUpdateTime.getTime()));
        return businessStudentOrderInfoRepository.search(qb, pageable);
    }

    @Override
    public Integer countByOrderPayTimeBetween(Date startTime, Date endTime) {
        return businessStudentOrderInfoRepository.countByOrderPayTimeBetween(startTime.getTime(), endTime.getTime());
    }

    @Override
    public Integer countByMysqlUpdateTimeBetween(Date startTime, Date endTime) {
        return businessStudentOrderInfoRepository.countByMysqlUpdateTimeBetween(startTime.getTime(), endTime.getTime());
    }

    @Override
    public Page<EsBusinessStudentOrderInfo> page(Date startTime, Date endTime, Pageable pageable) {

        RangeQueryBuilder rangequerybuilder = QueryBuilders
                .rangeQuery(orderPayTime)
                .gte(startTime.getTime())
                .lte(endTime.getTime());
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.search(rangequerybuilder, pageable);

        return page;
    }

    @Override
    public Page<EsBusinessStudentOrderInfo> pageBetweenMysqlUpdateTime(Date startTime, Date endTime, Pageable pageable) {
        RangeQueryBuilder rangequerybuilder = QueryBuilders
                .rangeQuery(mysqlUpdateTime)
                .gte(startTime.getTime())
                .lte(endTime.getTime());
        Page<EsBusinessStudentOrderInfo> page = businessStudentOrderInfoRepository.search(rangequerybuilder, pageable);

        return page;
    }

    @Override
    public void deleteAll(List<EsBusinessStudentOrderInfo> list) {
        businessStudentOrderInfoRepository.deleteAll(list);
    }


    /**
     * 按组搜索，构建搜索条件
     */
    private NativeSearchQuery buildGroupSearch(String lessonId, String startTime, String endTime) {

        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();

        //构建分组
        String term = teacherId;
        TermsAggregationBuilder aggBuilder = AggregationBuilders
                .terms(term).field(term)
                .subAggregation(sum(Fields.wechatCount).field(wechatCount))
                .subAggregation(count(Fields.orderCount).field(teacherId))
                .subAggregation(sum(Fields.totalCost).field(orderExpense))
                .subAggregation(sum(Fields.groupCount).field(addWechatGroupState))
                .subAggregation(sum(Fields.testCount).field(testCount))
                .subAggregation(sum(Fields.thisWeekApplyCount).field(bigLessonNumThisWeek))
                .subAggregation(sum(Fields.otherWeekApplyCount).field(bigLessonNumOtherWeek))
                .subAggregation(sum(Fields.miniIncome).field(orderPrice))
                .subAggregation(sum(Fields.salesCost).field(orderCost))
                .subAggregation(sum(Fields.thisWeekFlow).field(bigLessonFlowThisWeek))
                .subAggregation(sum(Fields.otherWeekFlow).field(bigLessonFlowOtherWeek))
                .subAggregation(sum(Fields.refundCount).field(bigLessonRefundNum))
                .subAggregation(sum(Fields.refundAmount).field(bigLessonRefundAmount))
                .size(Integer.MAX_VALUE);
        builder.addAggregation(aggBuilder);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //加入时间范围搜索
        if (StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) {
            Date s = DateUtil.parse(startTime, DatePattern.NORM_DATETIME_MINUTE_FORMAT);
            Date e = DateUtil.parse(endTime, DatePattern.NORM_DATETIME_MINUTE_FORMAT);
            RangeQueryBuilder rangequerybuilder = QueryBuilders
                    .rangeQuery(orderPayTime)
                    .from(s.getTime())
                    .to(e.getTime());
            boolQueryBuilder.must(rangequerybuilder);
        }

        if (StringUtils.isNotBlank(lessonId)) {
            boolQueryBuilder.must(QueryBuilders.termQuery(EsBusinessStudentOrderInfo.Fields.lessonId, lessonId));

        }
        builder.withQuery(boolQueryBuilder);
        builder.withIndices(EsBusinessStudentOrderInfo.index);
        return builder.build();

    }

    /**
     * 创建搜索结果
     */
    private MemberDataVO buildResult(LongTerms.Bucket bucket, Aggregations a) {
        return MemberDataVO.builder()
                .teacherId((int) Float.parseFloat(bucket.getKeyAsString()))
                .wechatCount((int) getFloatValue(a, Fields.wechatCount))
                .orderCount(getIntegerValue(a, Fields.orderCount))
                .groupCount((int) getFloatValue(a, Fields.groupCount))
                .totalCost(getFloatValue(a, Fields.totalCost))
                .testCount((int) getFloatValue(a, Fields.testCount))
                .thisWeekApplyCount((int) getFloatValue(a, Fields.thisWeekApplyCount))
                .otherWeekApplyCount((int) getFloatValue(a, Fields.otherWeekApplyCount))
                .miniIncome(getFloatValue(a, Fields.miniIncome))
                .salesCost(getFloatValue(a, Fields.salesCost))
                .thisWeekFlow(getFloatValue(a, Fields.thisWeekFlow))
                .otherWeekFlow(getFloatValue(a, Fields.otherWeekFlow))
                .refundCount((int) getFloatValue(a, Fields.refundCount))
                .refundAmount(getFloatValue(a, Fields.refundAmount))
                .build();
    }

    public static float getFloatValue(Aggregations a, String field) {
        Object result = common(a, field);
        return Float.parseFloat(String.valueOf(result));
    }

    public static int getIntegerValue(Aggregations a, String field) {
        Object result = common(a, field);
        return Integer.parseInt(String.valueOf(result));
    }

    //对返回值类型进行处理
    public static Object common(Aggregations a, String field) {
        Aggregation aggregation = a.get(field);
        Object result = null;
        if (aggregation instanceof Sum) {
            result = ((Sum) a.get(field)).getValueAsString();
        } else if (aggregation instanceof ValueCount) {
            result = ((InternalValueCount) a.get(field)).getValue();
        } else {
            try {
                throw new Exception("未处理的结果类型 " + aggregation.getType());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    @Autowired
    private EsBusinessStudentOrderInfoRepository businessStudentOrderInfoRepository;
    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

}
